﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Sinacor.OMM.DumpEntity;
using System.Threading;
using System.Diagnostics;

namespace Sinacor.OMM.DumpServer
{
    public class DumpWriter
    {

        private const string FOLDERNAME = "Dumper";
        private const int TIME_TO_RECYCLE_FILE = 60000;
        private static Dictionary<string, FlatFileWriter> _dctLogServer;
        private static Thread _threadRecycle;
        private static Mutex _mutex = new Mutex();
        private static bool _closing;
        private static bool _keepRunning;
        private static Tools.TraceWriter _traceWriter;

        private DumpWriter()
        {
            try
            {
                _traceWriter = Tools.Tracer.GetInstance(FOLDERNAME, "Dumper");
            }
            catch(Exception ex)
            {
                EventLog.WriteEntry("DumperWrite", ex.Message);
                EventLog.WriteEntry("DumperWrite", ex.StackTrace);
            }
        }

        public static FlatFileWriter GetInstance(string path, string fileName, DumpType.Type dumpType)
        {
            string key;
            FlatFileWriter ret;

            try
            {

                if (_mutex == null)
                    _mutex = new Mutex();

                _mutex.WaitOne();

                if (!_closing)
                {
                    if (_dctLogServer == null)
                        _dctLogServer = new Dictionary<string, FlatFileWriter>();

                    if (_threadRecycle == null) //Só abre 1 recycle independente do número de FlatFileWriters
                    {
                        _threadRecycle = new Thread(new ThreadStart(RecycleOpenedFiles));
                        _threadRecycle.Name = "Thread RecycleOpenedFiles";
                        _threadRecycle.Start();
                    }

                    //Chave que define a reutilização de um Handle de arquivo aberto
                    key = FlatFileWriter.GenerateKey(path, fileName, dumpType);

                    lock (_dctLogServer)
                    {
                        if (_dctLogServer.ContainsKey(key)) // Se já existe um handle para este arquivo (key), devolve o mesmo
                            ret = _dctLogServer[key];
                        else
                        { // Se não existe um handle para este arquivo (key), cria um novo
                            ret = new FlatFileWriter(path, fileName, dumpType);
                            _dctLogServer.Add(key, ret);
                        }
                    }
                }
                else
                    ret = null;

                _mutex.ReleaseMutex();
            }
            catch (Exception ex)
            {
                ret = null;
                EventLog.WriteEntry("DumperWrite", ex.Message);
                EventLog.WriteEntry("DumperWrite", ex.StackTrace);
            }

            return ret;
        }



        private static void KillInstance(string path, string fileName, DumpType.Type dumpType)
        {
            string key;

            if (!_closing)
            {
                if (_dctLogServer == null)
                    _dctLogServer = new Dictionary<string, FlatFileWriter>();

                key = FlatFileWriter.GenerateKey(path, fileName, dumpType);

                lock (_dctLogServer)
                {
                    if (_dctLogServer.ContainsKey(key))
                    {
                        _dctLogServer[key].Close();
                        _dctLogServer.Remove(key);
                    }
                }
            }
        }

        // Libera os arquivos abertos em FlatFileWriter, caso estes estejam sem atulização por "1" minuto.
        private static void RecycleOpenedFiles()
        {
            
            List<string> tempListKeys = new List<string>();
            Tools.TraceWriter traceWriter = Tools.Tracer.GetInstance(FOLDERNAME, "Recycle");

            _keepRunning = true;

            traceWriter = Tools.Tracer.GetInstance(FOLDERNAME, "Recycle");

            traceWriter.Trace("Entrou na Thread de reciclagem de recursos...");

            while (_keepRunning)
            {
                traceWriter = Tools.Tracer.GetInstance(FOLDERNAME, "Recycle");

                traceWriter.Trace("Aguardando " + TIME_TO_RECYCLE_FILE + "ms para começar o processo de reciclagem...", Tools.TraceType.Optional);
                //Já começa aguardando, pois não faz sentido reciclar logo no start...

                Tools.Sleeper.Sleep(TIME_TO_RECYCLE_FILE, IsRunning);

                if (_dctLogServer != null)
                {
                    lock (_dctLogServer)
                    {
                        if (traceWriter == null)
                        {
                            traceWriter = Tools.Tracer.GetInstance(FOLDERNAME, "Recycle");
                        }
                        traceWriter.Trace("Reciclando [" + _dctLogServer.Count + "] entradas de trace simultâneas", Tools.TraceType.Optional);
                    }
                }

                try
                {
                    if (_dctLogServer != null)
                    {
                        lock (_dctLogServer)
                        {
                            foreach (FlatFileWriter ffw in _dctLogServer.Values)
                            {
                                tempListKeys.Add(ffw.Key);
                            }

                            foreach(string tempKey in tempListKeys)
                            {   //Se um arquivo (key) está há mais de 1 minuto sem atualização, libera o handle desse arquivo
                                if (DateTime.Now.Subtract(_dctLogServer[tempKey].LastDumpedDataTimeStamp).TotalMilliseconds > TIME_TO_RECYCLE_FILE)
                                {
                                    _dctLogServer[tempKey].Close();
                                    _dctLogServer.Remove(tempKey);
                                    if (traceWriter == null)
                                    {
                                        traceWriter = Tools.Tracer.GetInstance(FOLDERNAME, "Recycle");
                                    }
                                    
                                    traceWriter.Trace("Reciclado.........[" + tempKey + "]", Tools.TraceType.Optional);
                                }
                            }

                            tempListKeys.Clear();
                        }
                    }

                    if (_dctLogServer != null)
                    {
                        lock (_dctLogServer)
                        {
                            if (traceWriter == null)
                            {
                                traceWriter = Tools.Tracer.GetInstance(FOLDERNAME, "Recycle");
                            }
                            traceWriter.Trace("Sobraram [" + _dctLogServer.Count + "] entradas de trace simultâneas após a reciclagem", Tools.TraceType.Optional);

                        }
                    }
                    
                    System.GC.Collect();

                    if (traceWriter == null)
                    {
                        traceWriter = Tools.Tracer.GetInstance(FOLDERNAME, "Recycle");
                    }
                    traceWriter.Trace("Recursos Reciclados!", Tools.TraceType.Optional);
                }
                catch (Exception ex)
                {
                    traceWriter.Trace(ex.Message);
                    traceWriter.Trace(ex.StackTrace);
                }

                if (traceWriter == null)
                {
                    traceWriter = Tools.Tracer.GetInstance(FOLDERNAME, "Recycle");
                }
                traceWriter.Trace("Processo de Reciclagem terminado!", Tools.TraceType.Optional);
            }
        }

        private static bool IsRunning()
        {
            return _keepRunning;
        }

        public static void Destroy()
        {
            _closing = true;
            _keepRunning = false;

            try
            {

                if(_mutex != null)
                    _mutex.WaitOne();

                if (_dctLogServer != null)
                {
                    lock (_dctLogServer)
                    {
                        foreach (FlatFileWriter logWriter in _dctLogServer.Values)
                        {
                            logWriter.Close();
                        }
                        _dctLogServer.Clear();
                    }
                }
            
                Tools.Tracer.Destroy();
            }
            catch (Exception ex)
            {
                EventLog.WriteEntry("DumperWrite", ex.Message);
                EventLog.WriteEntry("DumperWrite", ex.StackTrace);
            }

            if (_mutex != null)
            {
                _mutex.ReleaseMutex();
                _mutex.Close();
                _mutex = null;
            }
        }

    }
}
